package com.zjw.demo.vertx.starter.router;


import com.zjw.demo.vertx.starter.annotation.Nullable;
import com.zjw.demo.vertx.starter.annotation.web.Get;
import com.zjw.demo.vertx.starter.annotation.web.Header;
import com.zjw.demo.vertx.starter.annotation.web.Post;
import com.zjw.demo.vertx.starter.core.ioc.IOCScope;
import com.zjw.demo.vertx.starter.utils.LogUtils;
import com.zjw.demo.vertx.starter.utils.ThreadPool;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;

import java.lang.reflect.Method;

/**
 * 解析IRouter实现类中标记Web类注解的方法
 * 并将其添加到Router环境中，
 *
 * @see Get
 * @see Post
 * @see Header
 */
public final class RouterMethodParser {

  private static final String TAG = "RouterMethodParser";

  @Nullable
  private Router router;

  /**
   *
   */
  public void parse(Object iRouter) {
    if (router == null) {
      throw new IllegalStateException("router == null, un know prepareParse");
    }

    parseImpl(iRouter);
  }

  private void parseImpl(Object iRouter) {
    Method[] declaredMethods = iRouter.getClass().getDeclaredMethods();
    for (Method declaredMethod : declaredMethods) {
      //check params
      Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
      if (parameterTypes.length != 1) {
        continue;
      }
      Class<?> parameterType = parameterTypes[0];
      if (!RoutingContext.class.equals(parameterType)) {
        continue;
      }
      //check annotation
      if (declaredMethod.isAnnotationPresent(Get.class)) {
        parseGet(iRouter, declaredMethod);
      }
      if (declaredMethod.isAnnotationPresent(Post.class)) {
        parsePost(iRouter, declaredMethod);
      }
      if (declaredMethod.isAnnotationPresent(Header.class)) {
        parseHead(iRouter, declaredMethod);
      }
    }
  }

  private void parseHead(Object iRouter, Method method) {
    Header annotation = method.getAnnotation(Header.class);
    String value = annotation.value();
    router.head(value);
  }

  private void parsePost(Object iRouter, Method method) {
    Post annotation = method.getAnnotation(Post.class);
    boolean async = annotation.async();
    String path = annotation.path();
    router.post(path).handler(context -> {
      if (async) {
        ThreadPool.submit(() -> {
          doInvoke(method, iRouter, context);
        });
      } else {
        doInvoke(method, iRouter, context);
      }
    });
  }

  private void parseGet(Object iRouter, Method method) {
    Get annotation = method.getAnnotation(Get.class);
    boolean async = annotation.async();
    String path = annotation.path();
    router.get(path).handler(context -> {
      if (async) {
        ThreadPool.submit(() -> {
          doInvoke(method, iRouter, context);
        });
      } else {
        doInvoke(method, iRouter, context);
      }
    });
  }

  public void prepareParse(Router router) {
    LogUtils.debug(TAG, "prepareParse");
    this.router = router;
  }

  private void doInvoke(Method method, Object router, RoutingContext context) {
    try {
      Object bean = IOCScope.globalScope.getBean(router.getClass());
      method.invoke(bean, context);
    } catch (Exception e) {
      LogUtils.error(router.getClass().getName(), e);
    }
  }
}
